/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.mbds.wolf.db;

import org.mbds.wolf.exception.UserException;
import org.mbds.wolf.exception.UserExceptionReason;

/**
 *
 * @author amles_000
 */
public class DataStorageController {
    
    public final static short DB_ARRAY_SIZE_MAX = 256;
    public final static short DB_ARRAY_SIZE_INIT = 32;
    public final static short DB_ARRAY_SIZE_INC = 32;

    public final static byte[] DB_USER_TABLE_NAME = {'A', 'D', 'M', 'I', 'N'};
    public final static short DB_USER_TABLE_ID = 0x00;
    
    private byte[] adminLogin = {'a','d','m','i','n'};
    private byte[] adminPassword = {'a','d','m','i','n'};
    private DataTableUser adminUsers = null;
    private short masterAdminId = -1;

    private DataBase currentDB = null;
    private byte[] currentUser = null;
    private boolean isAdminUser = false;

    private DataBase[] databases = new DataBase[DB_ARRAY_SIZE_INIT];
    private short countDataBases = 0;

    private static DataStorageController instance = null;
    
    //==========================================================================//
    //                                                                          //
    //                         GETTING THE INSTANCE                             //
    //                                                                          //
    //==========================================================================//

    /**
     * getInstance
     * @param login
     * @param password
     * @return single instance
     * @throws UserException 
     */
    public static DataStorageController getInstance(byte[] login, byte[] password) 
            throws UserException {
        if (instance == null) {
            instance = new DataStorageController();
        }
        try {
            instance.currentDB = null;
            instance.isAdminUser = false;
            instance.currentUser = null;
            if (instance.checkAdminUser(login, password))
                instance.isAdminUser = true;
           if (!instance.isAdminUser)
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            instance.currentUser = login;
            return instance;
        } catch (UserException ex) {
            throw ex;
        }
    }
    
    /**
     * getInstance
     * @param login
     * @param password
     * @param dbName
     * @return single instance
     * @throws UserException 
     */
    public static DataStorageController getInstance(byte[] login, byte[] password, byte[] dbName) 
            throws UserException {
        if (instance == null) {
            instance = new DataStorageController();
        }
        try {
            instance.currentDB = null;
            instance.isAdminUser = false;
            instance.currentUser = null;
            if (instance.checkAdminUser(login, password))
                instance.isAdminUser = true;
            if (dbName!=null) {
                DataBase db = instance.getDataBaseByName(dbName);
                if (db!=null && (instance.isAdminUser || db.checkUser(login, password))) {
                    instance.currentDB = db;
                } else {
                    UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
                }
            } else {
                if (!instance.isAdminUser)
                    UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            }
            instance.currentUser = login;
            return instance;
        } catch (UserException ex) {
            throw ex;
        }
    }
    
    /**
     * getInstance
     * @param login
     * @param password
     * @param dbId
     * @return single instance
     * @throws UserException 
     */
    public static DataStorageController getInstance(byte[] login, byte[] password, short dbId) 
            throws UserException {
        if (instance == null) {
            instance = new DataStorageController();
        }
        try {
            instance.currentDB = null;
            instance.isAdminUser = false;
            instance.currentUser = null;
            if (instance.checkAdminUser(login, password))
                instance.isAdminUser = true;
            DataBase db = instance.getDataBaseById(dbId);
            if (db!=null && (instance.isAdminUser || db.checkUser(login, password))) {
                instance.currentDB = db;
            } else {
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            }
            instance.currentUser = login;
            return instance;
        } catch (UserException ex) {
            throw ex;
        }
    }
    
    //==========================================================================//
    //                                                                          //
    //                           PUBLIC METHODS                                 //
    //                                                                          //
    //==========================================================================//

    /**
     * getDataBaseName
     * @param org.mbds.wolf.db id
     * @return org.mbds.wolf.db name
     * @throws UserException 
     */
    public short getDataBaseName(short dbId) throws UserException {
        if (!isAdminUser)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        short index = searchDataBaseById(dbId);
        if (index==-1)
            UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
        return databases[index].id;
    }
    
    /**
     * getTableName
     * @param tableId
     * @return name
     */
    public byte[] getTableName(short tableId) throws UserException {
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return instance.getTableName(tableId);
    }
    
    /**
     * getColumnName
     * @param tableId
     * @param columnId
     * @return coulumn name
     * @throws UserException 
     */
    public byte[] getColumnName(short tableId, short columnId) throws UserException {
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return instance.getColumnName(tableId, columnId);
    }
    /**
     * getDataBaseId
     * @param dbName
     * @return org.mbds.wolf.db id
     * @throws UserException 
     */
    public short getDataBaseId(byte[] dbName) throws UserException {
        if (!isAdminUser)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        short index = searchDataBaseByName(dbName);
        if (index==-1)
            UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
        return databases[index].id;
    }
    
    /**
     * getTableId
     * @param tableName
     * @return tableId
     */
    public byte[] getTableId(byte[] tableName) throws UserException {
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return instance.getTableId(tableName);
    }
    
    /**
     * getColumnId
     * @param tableName
     * @param columnName
     * @return coulumn name
     * @throws UserException 
     */
    public byte[] getColumnId(byte[] tableName, byte[] columnName) throws UserException {
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return instance.getColumnId(tableName, columnName);
    }    
    /**
     * createUser
     * @param login
     * @param password
     * @param dbName
     * @param privileges
     * @return userId
     * @throws UserException 
     */
    public short createUser(byte[] login, byte[] password, DataInstructionType[] privileges) 
            throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.CREATE_USER.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (privileges != null) {
            for (short i = 0; i < privileges.length; i++) {
                if (isAdminUser) {
                    if (!adminUsers.checkUserPrivilege(currentUser, privileges[i].id())) {
                        UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
                    }
                } else {
                    if (!currentDB.checkUserPrivilege(currentUser, privileges[i].id())) {
                        UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
                    }
                }
            }
        }
        if (currentDB!=null) {
           currentDB.insert(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_LOGIN_ID, login);
           currentDB.insert(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_PASSWORD_ID, password);
           return currentDB.insert(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_PRIVILEGE_ID, privileges);
        } else {
           adminUsers.insert(DataTableUser.COL_LOGIN_ID, login);
           adminUsers.insert(DataTableUser.COL_PASSWORD_ID, password);
           return adminUsers.insert(DataTableUser.COL_PRIVILEGE_ID, privileges);
        }
    }
    
    /**
     * dropUser
     * @param login
     * @return boolean
     * @throws UserException 
     */
    public boolean dropUser(byte[] login) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.DROP_USER.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        short userId;
        short nb = 0;
        
        if (currentDB!=null) {
            userId = currentDB.getUserId(login);
            if (userId == -1)
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            nb = currentDB.delete(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_LOGIN_ID, userId);
            currentDB.delete(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_PASSWORD_ID, userId);
            currentDB.delete(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_PRIVILEGE_ID, userId);
        } else {
            userId = adminUsers.getUserId(login);
            if (userId == -1)
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            nb = adminUsers.delete(DataTableUser.COL_LOGIN_ID, userId);
            adminUsers.delete(DataTableUser.COL_PASSWORD_ID, userId);
            adminUsers.delete(DataTableUser.COL_PRIVILEGE_ID, userId);
        }
            return nb>0;
    }
    
    /**
     * grantPrivilege
     * @param userLogin
     * @param privilege
     * @return boolean
     * @throws UserException 
     */
    public boolean grantPrivilege(byte[] login, DataInstructionType privilege) 
            throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.GRANT_PRIVILEGE.id()) 
                || !checkCurrentUserPrivilege(privilege.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        short userId;
        byte[] privileges;
        if (currentDB!=null) {
            userId = currentDB.getUserId(login);
            if (userId == -1)
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            privileges = currentDB.select(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_PRIVILEGE_ID, userId);
            privileges = DataTool.addEntry(privileges, privilege.id());
            return (currentDB.update(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_PRIVILEGE_ID, userId, privileges))>0;
        } else {
            userId = adminUsers.getUserId(login);
            if (userId == -1)
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            privileges = adminUsers.select(DataTableUser.COL_PRIVILEGE_ID, userId);
            privileges = DataTool.addEntry(privileges, privilege.id());
            return (adminUsers.updateData(DataTableUser.COL_PRIVILEGE_ID, userId, privileges))>0;
        }        
    }
    /**
     * revokePrivilege
     * @param userLogin
     * @param privilege
     * @return boolean
     * @throws UserException 
     */
    public boolean revokePrivilege(byte[] login, DataInstructionType privilege) 
            throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.REVOKE_PRIVILEGE.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        short userId;
        byte[] privileges;
        if (currentDB!=null) {
            userId = currentDB.getUserId(login);
            if (userId == -1)
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            privileges = currentDB.select(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_PRIVILEGE_ID, userId);
            privileges = DataTool.removeEntry(privileges, privilege.id());
            return (currentDB.update(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_PRIVILEGE_ID, userId, privileges))>0;
        } else {
            userId = adminUsers.getUserId(login);
            if (userId == -1)
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            privileges = adminUsers.select(DataTableUser.COL_PRIVILEGE_ID, userId);
            privileges = DataTool.removeEntry(privileges, privilege.id());
            return (adminUsers.updateData(DataTableUser.COL_PRIVILEGE_ID, userId, privileges))>0;
        }   
    }
    
    /**
     * alterUser
     * @param userLogin
     * @param password
     * @return boolean
     * @throws UserException 
     */
    public boolean alterUser(byte[] login, byte[] password) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.ALTER_USER.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        short userId;
        short nb = 0;
        if (currentDB!=null) {
            userId = currentDB.getUserId(login);
            if (userId == -1)
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            nb = currentDB.update(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_PASSWORD_ID, userId, password);
        } else {
            userId = adminUsers.getUserId(login);
            if (userId == -1)
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            nb = adminUsers.updateData(DataTableUser.COL_PASSWORD_ID, userId, password);
        } 
        return nb>0;
    }
    
    /**
     * listPrivileges
     * @param userLogin
     * @return DataInstructionType[]
     * @throws UserException 
     */
    public byte[] listPrivileges(byte[] login)throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.LIST_PRIVILEGE.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        short userId;
        byte[] privileges;
        if (currentDB!=null) {
            userId = currentDB.getUserId(login);
            if (userId == -1)
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            privileges = currentDB.select(DataBase.DB_USER_TABLE_ID, DataTableUser.COL_PRIVILEGE_ID, userId);
        } else {
            userId = adminUsers.getUserId(login);
            if (userId == -1)
                UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
            privileges = adminUsers.select(DataTableUser.COL_PRIVILEGE_ID, userId);
        } 
        return privileges;
    }
    
    /**
     * createDataBase
     * @param dbName
     * @return id
     * @throws UserException 
     */
    public short createDataBase(byte[] dbName) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.CREATE_DB.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        short id = countDataBases;
        createDataBase(id, dbName);
        return id;
    }
    
    /**
     * createTable
     * @param tableName
     * @return id
     * @throws UserException 
     */
    public short createTable(byte[] tableName) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.CREATE_TABLE.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        short id = currentDB.countTables();
        currentDB.createTable(id, tableName);
        return id;
    }
    
    /**
     * addColumn
     * @param tableName
     * @param columnName
     * @return id
     * @throws UserException 
     */
    public short addColumn(byte[] tableName, byte[] columnName) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.ADD_COLUMN.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        short id = currentDB.countColumns(tableName);
        currentDB.addColumn(tableName, id, columnName);
        return id;
    }
    
    /**
     * dropDataBase
     * @param dbName
     * @return boolean
     * @throws UserException 
     */
    public boolean dropDataBase(byte[] dbName) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.DROP_DB.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        DataBase db = getDataBaseByName(dbName);
        if (db==null)
            UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
        return db.drop();
    }
    
    /**
     * dropTable
     * @param tableName
     * @return boolean
     * @throws UserException 
     */
    public boolean dropTable(byte[] tableName) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.DROP_TABLE.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return currentDB.dropTable(tableName);
    }
    
    /**
     * dropColumn
     * @param tableName
     * @param columnName
     * @return boolean
     * @throws UserException 
     */
    public boolean dropColumn(byte[] tableName, byte[] columnName)throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.DROP_COLUMN.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return currentDB.dropColumn(tableName, columnName);
    }
    /**
     * dropDataBase
     * @param dbId
     * @return boolean
     * @throws UserException 
     */
    public boolean dropDataBase(short dbId) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.DROP_DB.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        DataBase db = getDataBaseById(dbId);
        if (db==null)
            UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
        return db.drop();
    }
    
    /**
     * dropTable
     * @param tableId
     * @return boolean
     * @throws UserException 
     */
    public boolean dropTable(short tableId) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.DROP_TABLE.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return currentDB.dropTable(tableId);
    }
    
    /**
     * dropColumn
     * @param tableId
     * @param columnId
     * @return boolean
     * @throws UserException 
     */
    public boolean dropColumn(short tableId, short columnId)throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.DROP_COLUMN.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return currentDB.dropColumn(tableId, columnId);
    }
    
    /**
     * renameDataBase
     * @param newName
     * @return boolean
     * @throws UserException 
     */
    public boolean renameDataBase(byte[] newName) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.RENAME_DB.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        currentDB.rename(newName);
        return true;
    }
    
    /**
     * renameTable
     * @param tableName
     * @param newName
     * @return boolean
     * @throws UserException 
     */
    public boolean renameTable(byte[] tableName, byte[] newName) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.RENAME_TABLE.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return currentDB.renameTable(tableName, newName);
    }
    /**
     * renameTable
     * @param tableId
     * @param newName
     * @return boolean
     * @throws UserException 
     */
    public boolean renameTable(short tableId, byte[] newName) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.RENAME_TABLE.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return currentDB.renameTable(tableId, newName);
    }
    /**
     * renameColumn
     * @param tableId
     * @param columnId
     * @param newName
     * @return boolean
     * @throws UserException 
     */
    public boolean renameColumn(short tableId, short columnId, byte[] newName) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.RENAME_COLUMN.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return currentDB.renameColumn(tableId, columnId, newName);
    }
    
    /**
     * select
     * @param tableName
     * @param columnName
     * @param recordId
     * @return data
     * @throws UserException 
     */
    public byte[] select(byte[] tableName, byte[] columnName, short recordId) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.SELECT.id())) 
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        if (columnName==null)
            UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
        return currentDB.select(tableName, columnName, recordId);
    }
    /**
     * select
     * @param tableId
     * @param columnId
     * @param recordId
     * @return data
     * @throws UserException 
     */
    public byte[] select(short tableId, short columnId, short recordId) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.SELECT.id())) 
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return currentDB.select(tableId, columnId, recordId);
    }
    /**
     * insert
     * @param tableName
     * @param columnName
     * @param recordData
     * @return id
     * @throws UserException 
     */
    public short insert(byte[] tableName, byte[] columnName, byte[] recordData) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.INSERT.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        if (columnName==null)
            UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
        return currentDB.insert(tableName, columnName, recordData);
    }
    /**
     * insert
     * @param tableId
     * @param columnId
     * @param recordData
     * @return id
     * @throws UserException 
     */
    public short insert(short tableId, short columnId, byte[] recordData) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.INSERT.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return currentDB.insert(tableId, columnId, recordData);
    }  
    
    /**
     * update
     * @param tableName
     * @param columnName
     * @param recordId
     * @param newValue
     * @return nb records updated
     * @throws UserException 
     */
    public short update(byte[] tableName, byte[] columnName, short recordId, byte[] newValue) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.UPDATE.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        if (columnName==null)
            UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
        return currentDB.update(tableName, columnName, recordId, newValue);
    }
    
    /**
     * update
     * @param tableId
     * @param columnId
     * @param recordId
     * @param newValue
     * @return nb records updated
     * @throws UserException 
     */
    public short update(short tableId, short columnId, short recordId, byte[] newValue) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.UPDATE.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return currentDB.update(tableId, columnId, recordId, newValue);
    }
    
    /**
     * delete
     * @param tableName
     * @param columnName
     * @param id
     * @return nb records deleted
     * @throws UserException 
     */
    public short delete(byte[] tableName, byte[] columnName, short recordId) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.DELETE.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        if (columnName==null)
            UserException.throwIt(UserExceptionReason.INVALID_ARGUMENT_EXCEPTION);
        return currentDB.delete(tableName, columnName, recordId);
    }
    
    /**
     * delete
     * @param tableId
     * @param columnId
     * @param id
     * @return nb records deleted
     * @throws UserException 
     */
    public short delete(short tableId, short columnId, short recordId) throws UserException {
        if (!checkCurrentUserPrivilege(DataInstructionType.DELETE.id())) {
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        if (currentDB==null)
            UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        return currentDB.delete(tableId, columnId, recordId);
    }
    
    //==========================================================================//
    //                                                                          //
    //                          PRIVATE METHODS                                 //
    //                                                                          //
    //==========================================================================//
    
    /**
     * checkAdminUser
     * @param login
     * @param password
     * @return boolean
     */
    private boolean checkAdminUser(byte[] login, byte[] password) {
        return adminUsers.checkUser(login, password);
    }
    
    /**
     * checkCurrentUserPrivilege
     * @param privilegeId
     * @return boolean
     * @throws UserException 
     */
    private boolean checkCurrentUserPrivilege(short privilegeId) throws UserException {
        if (isAdminUser) {
            if (!adminUsers.checkUserPrivilege(currentUser, privilegeId)) 
                UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        } else {
            if (!currentDB.checkUserPrivilege(currentUser, privilegeId))
                UserException.throwIt(UserExceptionReason.DB_UNHAUTORIZED_INSTRUCTION_EXCEPTION);
        }
        return true;
    }

    /**
     * createDataBase
     * @param id
     * @param name
     * @throws UserException 
     */
    private void createDataBase(final short id, final byte[] name) throws UserException {
        byte[] dbName = name;
//        byte[] dbName = null;
//        try {
//        //TODO: Table name should be char types without space only   
//            dbName = DataTool.trim(name);
//        } catch (UserException e) {
// //           UserException.throwIt(e.getReason());
//        }
        boolean isFoundId = searchDataBaseById(id)>=0;
        boolean isFoundName = searchDataBaseByName(dbName)>=0;
        if (isFoundId && isFoundName)
            UserException.throwIt(UserExceptionReason.DB_ATOMIC_DATA_ALREADY_EXIST);
        if (isFoundId)
            UserException.throwIt(UserExceptionReason.DB_ATOMIC_DATA_ID_ALREADY_USED);
        if (isFoundName)
            UserException.throwIt(UserExceptionReason.DB_ATOMIC_DATA_NAME_ALREADY_USED);
        if ((countDataBases + 1 > databases.length) && !increaseDatabaseArray(DB_ARRAY_SIZE_INC))
            UserException.throwIt(UserExceptionReason.ARRAY_INDEX_OUT_OF_BOUND);
        DataBase db = new DataBase(id, dbName);
        databases[countDataBases] = db;
        countDataBases++;    
    }    

    /**
     * searchDataBaseById
     * @param id
     * @return short index, -1 if not found
     */
    private short searchDataBaseById(short id) {
        return DataTool.containsId(id, databases);
    }
    
    /**
     * searchDataBaseByName
     * @param id
     * @return short index, -1 if not found
     * @throws UserException 
     */
    private short searchDataBaseByName(final byte[] name) throws UserException {
        try {
            return DataTool.containsValue(name, databases);
        } catch (UserException e) {
            throw e;
        }
     }    
    /**
     * getDataBaseByName
     * @param dbName
     * @return DataBase or null
     */
    private DataBase getDataBaseByName(byte[] dbName) {
        for (short i=0;i<databases.length;i++)
            if (databases[i]!=null && databases[i].value().length==dbName.length
                    && DataTool.arrayCompare(databases[i].value, (short)0, dbName, (short)0, (short)dbName.length)==0)
                return databases[i];
        return null;
    }
    /**
     * getDataBaseById
     * @param dbId
     * @return DataBase or null
     */
    private DataBase getDataBaseById(short dbId) {
        for (short i=dbId;i>=0;i--)
            if (databases[i]!=null && databases[i].id==dbId)
                return databases[i];
        return null;
    }
    
    /**
     * increaseDatabaseArray
     * @param inc
     * @return boolean
     */
    private boolean increaseDatabaseArray(short inc) {
        if (databases.length + inc > DB_ARRAY_SIZE_MAX) {
            return false;
        }
        DataBase[] newDBs = new DataBase[(short) (databases.length + inc)];
        for (short i = 0; i < databases.length; i++) {
            newDBs[i] = databases[i];
        }
        databases = newDBs;
        return true;
    } 

    /**
     * Private constructor
     */
    private DataStorageController() {
        try {
            byte[] allPrivileges = {
                DataInstructionType.SELECT.id(),
                DataInstructionType.INSERT.id(),
                DataInstructionType.UPDATE.id(),
                DataInstructionType.DELETE.id(),
                DataInstructionType.CREATE_DB.id(),
                DataInstructionType.CREATE_TABLE.id(),
                DataInstructionType.CREATE_USER.id(),
                DataInstructionType.ALTER_USER.id(),
                DataInstructionType.ADD_COLUMN.id(),
                DataInstructionType.DROP_DB.id(),
                DataInstructionType.DROP_TABLE.id(),
                DataInstructionType.DROP_COLUMN.id(),
                DataInstructionType.DROP_USER.id(),
                DataInstructionType.GRANT_PRIVILEGE.id(),
                DataInstructionType.LIST_PRIVILEGE.id(),
                DataInstructionType.REVOKE_PRIVILEGE.id(),
                DataInstructionType.RENAME_DB.id(),
                DataInstructionType.RENAME_TABLE.id(),
                DataInstructionType.RENAME_COLUMN.id()};
            adminUsers = new DataTableUser(DB_USER_TABLE_ID, DB_USER_TABLE_NAME);
            masterAdminId = adminUsers.insert(DataTableUser.COL_LOGIN_ID, adminLogin);
            adminUsers.insert(DataTableUser.COL_PASSWORD_ID, adminPassword);
            adminUsers.insert(DataTableUser.COL_PRIVILEGE_ID, allPrivileges);
        } catch (UserException ex) {
            ex.printStackTrace();
        }
    }
    
    
}
